@node-ts/bus-messages
This package should be consumed wherever your application defines message contracts. Messages are small pieces of data that get passed around between services. They can define an instruction to perform an action, or report that something has just occurred.
@node-ts/bus-messages
defines two types of messages:
Command
Commands are a type of message that represents an instruction to do work. These can be technical instructions such as BackupDatabase
, ScaleOutLoadBalancer
, or modeled after your business domains like ChargeCreditCard
, PostMerchandise
.
Commands should be named as an instruction, using plain english. The above commands are understandable in plain English that they will do some sort of action.
To implement a comamnd, simply extend Command
and add in the fields that are relevant to it:
import { Command } from '@node-ts/bus-messages'
export class ChargeCreditCard extends Command {
$name = 'my-app/accounts/charge-credit-card'
$version = 1
constructor (
readonly creditCardId: string,
readonly amount: number
) {
}
}
To send a command, just use bus.send()
, eg:
import { Bus } from '@node-ts/bus-core'
async function chargeCreditCard (
bus: Bus,
creditCardId: string,
amount: number
): Promise<void> {
const command = new ChargeCreditCard(creditCardId, amount)
await this.bus.send(command)
}
A commands are sent to a single service for processing, and generally result in the publication of one or more Events
Event
An event is a message emitted by the system when "something" happens. Again this could be a technical task being completed such as DatabaseBackedup
, LoadBalancerScaledOut
or as a result of changes in your business CreditCardCharged
, MerchandisePosted
.
Past-tense should be used when naming an event, since it indicates that an operation was performed on your application.
Events are class definitions that extend from Event
, eg:
import { Event } from '@node-ts/bus-messages'
export class CreditCardCharged extends Event {
$name = 'my-app/accounts/credit-card-charged'
$version = 1
constructor (
readonly creditCardId: string,
readonly amount: number
) {
}
}
To publish an event, just use bus.publish()
, eg:
import { Bus } from '@node-ts/bus-core'
async function creditCardChaged (
bus: Bus,
creditCardId: string,
amount: number
): Promise<void> {
const event = new CreditCardCharged(creditCardId, amount)
await this.bus.publish(event)
}
Usage
Modelling your system using messages like the ones above is very powerful. There are no concerns around what handles them or how they're processed - they just represent an intent to change the system (command
) or that a change in the system has occurred (event
).
Over time the number of messages in your library will build up to describe the business domain you're modeling. The messages can be easily defined and well documented so that new developers can understand what they do without any technical detail, which encourages a well-documented system in code.
Message Attributes
Additional metadata can be added to any type of message as attributes alongside the actual message.
When sending via a transport (eg: SQS, RabbitMQ) the message is sent in a message envelope. Commands and Events are serialized into the message body, whilst attributes are added to the message header.
Attributes are designed to hold data that is related to technical concerns of routing the message, or auditing/logging information such as details around the originator.